package com.mercycrazy.recruit.util.jwt;

import com.alibaba.fastjson2.JSON;
import com.mercycrazy.recruit.util.IdUtil;
import com.mercycrazy.recruit.util.LocalDateUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Encoder;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;

import static com.mercycrazy.recruit.constant.CommonConstant.DEFAULT_JWT_EXPIRE_TIME;
import static com.mercycrazy.recruit.constant.CommonConstant.DEFAULT_JWT_SECRET;

/**
 * Jwt 工具类
 *
 * @author <a href="mailto:1443424326@qq.com">mercycrazy</a>
 * @since 2023-01-15
 */
@SuppressWarnings("all")
public class JWTUtil {

    private static final String AT = "@";

    public static String generateTokenWithPrefix(Map<String, ?> content, Integer expire,
                                                 String secretKeyStr, String prefix) {

        return prefix + AT + generateToken(content, expire, secretKeyStr);
    }

    public static String generateTokenWithPrefix(Map<String, ?> content, Integer expire,
                                                 String prefix) {

        return prefix + AT + generateToken(content, expire);
    }

    public static String generateTokenWithPrefix(Map<String, ?> content, String prefix) {

        return prefix + AT + generateToken(content);
    }

    public static String generateToken(Map<String, ?> content) {

        return generateToken(content, DEFAULT_JWT_EXPIRE_TIME);
    }

    public static String generateToken(Map<String, ?> content, Integer expire) {

        return generateToken(content, expire, DEFAULT_JWT_SECRET);
    }

    /**
     * 生成 JWT TOKEN
     *
     * @param content      payload
     * @param expire       过期时间(单位: 秒)
     *                     null 表示永不过期
     * @param secretKeyStr 加密字符串
     */
    public static String generateToken(Map<String, ?> content, Integer expire,
                                       String secretKeyStr) {

        if (StringUtils.isBlank(secretKeyStr)) {
            secretKeyStr = DEFAULT_JWT_SECRET;
        }

        Date expireDate = null;

        if (expire != null) {
            if (expire <= 0) {
                expire = DEFAULT_JWT_EXPIRE_TIME;
            }
            // 计算超时时间
            expireDate = LocalDateUtils.asDate(
                    LocalDateUtils.plus(LocalDateTime.now(), expire, ChronoUnit.SECONDS)
            );
        }

        // 签名
        SecretKey secretKey = generateHmacSecretKey(secretKeyStr);

        return Jwts.builder()
                // jwt payload --> KV
                .setClaims(content)
                // jwt id
                .setId(IdUtil.generateUUID())
                // jwt 过期时间
                .setExpiration(expireDate)
                // jwt 签名 --> 加密
                .signWith(secretKey)
                .compact();
    }

    public static Map<String, ?> checkAndParseToken(String token) {

        return checkAndParseTokenWithPrefix(token, DEFAULT_JWT_SECRET);
    }

    public static Map<String, ?> checkAndParseTokenWithPrefix(String token) {

        return checkAndParseTokenWithPrefix(token, DEFAULT_JWT_SECRET);
    }

    public static Map<String, ?> checkAndParseTokenWithPrefix(String token, String secretKeyStr) {

        int index = token.indexOf("@");

        if (index >= 0) {
            token = token.substring(index + 1, token.length());
        }

        return checkAndParseToken(token, secretKeyStr);
    }

    /**
     * 无论 JWT Token 不合法、为空、过期都会抛出 JWTException
     * 外部 handler JWTException 即可, 这里无需处理
     */
    public static Map<String, ?> checkAndParseToken(String token, String secretKeyStr) {

        if (StringUtils.isBlank(secretKeyStr)) {
            secretKeyStr = DEFAULT_JWT_SECRET;
        }

        SecretKey secretKey = generateHmacSecretKey(secretKeyStr);

        JwtParser parser = Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build();

        Jws<Claims> claimsJws = parser.parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (Map<String, ?>) JSON.parseObject(
                JSON.toJSONString(claims), Map.class
        );
    }

    private static SecretKey generateHmacSecretKey(String secretKeyStr) {

        String base64SecretStr = new BASE64Encoder().encode(
                secretKeyStr.getBytes(StandardCharsets.UTF_8));
        return Keys.hmacShaKeyFor(base64SecretStr.getBytes(StandardCharsets.UTF_8));
    }


}
